package com.example.librarymanagementproject2.Common.utils;

import com.example.librarymanagementproject2.Common.config.MinioConfig;
import com.example.librarymanagementproject2.Common.exception.GlobalException;
import io.minio.*;
import io.minio.http.Method;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName MinioUtil
 * @Description TODO
 * @Author Kx
 * @Date 2025/10/26 19:58
 * @Version 1.0
 */
@Component
@RequiredArgsConstructor
public class MinioUtil {
    private final MinioConfig minioConfig;
    private final MinioClient minioClient;
    private final RedisTemplate<String, String> redisTemplate;
    /**
     * 存储文件的 Hash 值的键前缀
    */
    private final static String FILE_HASH_PREFIX = "minio:file:hash:";

    /**
     * @author: kx
     * @date: 2025/10/26 20:05
     * @description: 初始化 Minio 存储桶
    */
    public void initBucket() {
        try {
            if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(minioConfig.getBucketName()).build())) {
                // 创建存储桶
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(minioConfig.getBucketName()).build());
            }
        } catch (Exception e) {
            throw new GlobalException(500, "初始化 Minio 存储桶失败");
        }
    }

    /**
     * @param file : 文件
     * @return java.lang.String
     * @author: kx
     * @date: 2025/10/26 20:15
     * @description: 上传图片
     */
    public String uploadImage(MultipartFile file) {
        // 初始化 Minio 存储桶
        initBucket();
        // 判断文件是否存在
        if (file.isEmpty()) {
            throw new GlobalException(500, "上传文件不能为空");
        }

        // 计算文件 Hash 值
        String fileHash = FileHashUtil.calculateMd5Hash(file);

        // 判断缓存中是否存在该图片的哈希值
        String existingFileName = redisTemplate.opsForValue().get(FILE_HASH_PREFIX + fileHash);
        // 如果存在直接返回文件名
        if (existingFileName != null) {
            return existingFileName;
        }

        // 获取文件名
        String originalFileName = file.getOriginalFilename();
        // 获取文件后缀
        String suffix = originalFileName.substring(originalFileName.lastIndexOf("."));
        // 生成随机文件名
        String fileName = UUID.randomUUID().toString() + suffix;

        // 循环判断文件名是否存在
        do {
            fileName = UUID.randomUUID().toString() + suffix;
        } while (isFileExists(fileName));

        // 上传文件
        try (InputStream inputStream = file.getInputStream()) {
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(minioConfig.getBucketName())
                            .object(fileName)
                            .stream(inputStream, file.getSize(), -1)
                            .contentType(file.getContentType())
                            .build()
            );

            // 把该图片的哈希值和文件名存储到缓存中
            redisTemplate.opsForValue().set(FILE_HASH_PREFIX + fileHash, fileName, 365, TimeUnit.DAYS);
        } catch (Exception e) {
            throw new GlobalException(500, "上传文件失败");
        }
        // 返回文件名
        return fileName;
    }

    /**
     * @author: kx
     * @date: 2025/10/26 20:28
     * @param fileName : 文件名
     * @return boolean
     * @description: 判断文件名是否存在
    */
    private boolean isFileExists(String fileName) {
        try {
            minioClient.statObject(StatObjectArgs.builder().bucket(minioConfig.getBucketName()).object(fileName).build());
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * @author: kx
     * @date: 2025/10/27 20:45
     * @param imageName : 图片名
     * @return java.lang.String
     * @description: 根据图片名称获取访问图片的 url 路径
    */
    public String getImageUrl(String imageName) {
        try {
            // 判断图片是否存在 minio 中
            if (!isFileExists(imageName)) {
                throw new GlobalException(500, "图片不存在");
            }

            // 调用 Minio 中的 API 生成临时的 url 访问路径
            return minioClient.getPresignedObjectUrl(
                    GetPresignedObjectUrlArgs.builder()
                            .method(Method.GET) // 访问方式：GET 请求
                            .bucket(minioConfig.getBucketName()) // 存储桶名称
                            .object(imageName) // Minio中的文件名
                            .expiry(3600, TimeUnit.SECONDS) // 访问有效期: 1 小时
                            .build()
            );
        }catch (Exception e) {
            throw new GlobalException(500, "获取图片 url 失败" + e.getMessage());
        }
    }
}
